﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Tycho.Runtime;
using Tycho.Parser.Tokens;
using Tycho.Compiler;
using Tycho.Language;

namespace Tycho.Parser {
    public class MacroBuilder {
        AnyObject RuntimeContext;
        AnyObject GeneratedModule;
        ModuleMap ModuleMap;

        public MacroBuilder (AnyObject runtimeContext) {
            RuntimeContext = runtimeContext;
            GeneratedModule = CompilerModule.CreateList (Namespace.NextGeneratedName ());
            ModuleMap = new ModuleMap (Namespaces.Parser);
        }

        TermGenerator UnquoteExpression (AnyObject expression) {
            return new QuoteVariableGenerator (ModuleMap.ExpectSymbol (expression));
        }

        public AnyObject BuildMacro (IEnumerable<AnyObject> productionNames, SourceLocation sloc, AnyObject macroBody, bool isQuote) {
            if (isQuote) {
                var builder = new TermGeneratorBuilder (GeneratedModule);
                return new MacroOperation (builder.Build (macroBody, UnquoteExpression));
            } else {
                var scope = new FrameSymbolScope (ExpressionLanguage.BuildScopeRecursive (RuntimeContext));

                AnyObject parameters = CompilerModule.CreateTerm (sloc);
                foreach (AnyObject production in productionNames) {
                    parameters.SetProperty (production, new MatchingSchemaObject (production, 0));
                    scope.DeclareVariable (production, sloc);
                }

                var expressionCompiler = new ExpressionCompiler (Namespaces.Parser);
                AnyObject bodyExpression = expressionCompiler.Compile (macroBody, scope, false);

                var byteCodeCompiler = new ByteCodeCompiler ();
                ByteCodeOutput bodyByteCode = byteCodeCompiler.GenerateCode (bodyExpression);

                return new ClosureObject (bodyByteCode.GetCode (), bodyByteCode.GetConstants (), parameters, new ModuleSecurityObject (Namespaces.Parser, RuntimeContext), sloc);
            }
        }
    }
}
